﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Text;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Administration.Health;

namespace SPHealth.SharePoint.HealthRules.SP.Rules.Configuration
{
    /*
     NOTE: There is already a like rule OOTB however it checks for 100GB, 200GB is the new 100GB. 
     */

    public class WarnContentDatabaseSize : SPHealthAnalysisRule
    {
        #region Locals
        private const string MaxRecomendedContentDBSizeGB = "MaxRecomendedContentDBSizeGB";
        private const string ruleTitle = "WarnContentDatabaseSize";

        private const long KB = 1024;
        private const long MB = 1024 * KB;
        private const long GB = 1024 * MB;

        private readonly StringBuilder _failingContentDatabases;
        private int _numFailingDatabases;
        private SPHealthAnalysisRuleAutomaticExecutionParameters _executionParameters;
        #endregion

        #region Constructor

        public WarnContentDatabaseSize()
        {
            _failingContentDatabases = new StringBuilder();
        }
        #endregion
        
        #region Fields

        public override string Summary
        {
            get { return "One or more Content Databases are very large."; }
        }
        
        public override string Explanation
        {
            get 
            { 
                return String.Format("The following {0} Content DBs are over the recomended size of {1} GB: {2}.", 
                                        _numFailingDatabases, common.GetRuleSetting(MaxRecomendedContentDBSizeGB,ruleTitle), _failingContentDatabases); 
            }
        }

        public override string Remedy
        {
            get { return "Add additional content Databases and redistribute sites."; }
        }

        public override SPHealthCheckErrorLevel ErrorLevel
        {
            get { return SPHealthCheckErrorLevel.Warning; }
        }

        public override SPHealthCategory Category
        {
            get { return SPHealthCategory.Configuration; }
        }

        public override SPHealthAnalysisRuleAutomaticExecutionParameters AutomaticExecutionParameters
        {
            get
            {
                if (_executionParameters == null)
                {
                    _executionParameters = new SPHealthAnalysisRuleAutomaticExecutionParameters
                    {
                        Schedule = SPHealthCheckSchedule.Daily,
                        Scope = SPHealthCheckScope.Any,
                        ServiceType = typeof(SPTimerService), //Tell SP if we want our own timer job or can we run with others...
                        RepairAutomatically = false
                    };
                }
                return _executionParameters;
            }
        }
        
        #endregion

        #region Methods

        // used to provide the configuration list item title
        public static string GetTitle()
        {
            // return the title for the rule
            return ruleTitle;
        }

        // used to provide the default configuration settings
        public static IDictionary<string, string> GetDefaultSettings()
        {
            // return the default settings for the rule
            IDictionary<string, string> settings = new Dictionary<string, string>();
            settings.Add("Comment1", "Enter the maximum supported size for a content database");
            settings.Add(MaxRecomendedContentDBSizeGB, "200");
            return settings;
        }

        public override SPHealthCheckStatus Check()
        {
            if (!SPFarm.Joined)
            {
                throw new InvalidOperationException();
            }
            
            SPHealthCheckStatus status = SPHealthCheckStatus.Passed;

            foreach (SPWebApplication webApplication in SPWebService.ContentService.WebApplications)
            {
                if (webApplication.Status == SPObjectStatus.Online)
                {
                    foreach (SPContentDatabase contentDatabase in webApplication.ContentDatabases)
                    {
                        try
                        {
                            long contentDatabaseSize = GetContentDatabaseSize(contentDatabase);
                            if (contentDatabaseSize > 
                                (Convert.ToInt64(common.GetRuleSetting(MaxRecomendedContentDBSizeGB,ruleTitle))) * GB
                                )
                            {
                                status = SPHealthCheckStatus.Failed;
                                _numFailingDatabases++;
                                _failingContentDatabases.AppendLine(String.Format("{0} ({1} GB) ", contentDatabase.Name, (contentDatabaseSize / GB)));
                            }
                        }
                        catch {/*Best effort*/}
                    }
                }
            }

            return status;
        }

        private static long GetContentDatabaseSize(SPContentDatabase contentDatabase)
        {
            DataSet resultSet;
            long databaseSize = 0;

            using (SqlConnection connection = new SqlConnection(contentDatabase.DatabaseConnectionString))
            {
                connection.Open();
                using (SqlCommand command = new SqlCommand { Connection = connection, CommandText = String.Format("Use Master; exec sp_helpdb '{0}'", contentDatabase.Name) })
                {
                    using (SqlDataAdapter adapter = new SqlDataAdapter(command))
                    {
                        resultSet = new DataSet();
                        adapter.Fill(resultSet);
                    }
                }
            }

            if (resultSet != null && resultSet.Tables.Count == 2 && resultSet.Tables[0].Rows.Count == 1)
            {
                string size = ((String)resultSet.Tables[0].Rows[0]["db_size"]).Trim();
                string[] sizeParts = size.Split(' ');

                if (sizeParts.Length == 2)
                {
                    double parseResult = 0;

                    if (Double.TryParse(sizeParts[0], out parseResult))
                    {

                        switch (sizeParts[1])
                        {
                            case "KB":
                                databaseSize = (long)(parseResult * KB);
                                break;

                            case "MB":
                                databaseSize = (long)(parseResult * MB);
                                break;

                            case "GB":
                                databaseSize = (long)(parseResult * GB);
                                break;
                            
                            default:
                                break;
                        }
                    }
                }
            }

            return databaseSize;
        }
        #endregion
    }
}
